//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"

//SAGITTARIUS headers:
#include "sagittarius/re/reParser.h"

#include <stdio.h>
//#include <time.h>
#include <malloc.h>
//#include <string.h>

//consts are incompatible with switch statements
//have to use #define
/*const UnicodeCChar CONST_AMP    =   0x26;//'&'
const UnicodeCChar CONST_LPAREN =   0x28;//'('
const UnicodeCChar CONST_RPAREN =   0x29;//')'
const UnicodeCChar CONST_VBAR   =   0x7C;//'|'*/

#define CONST_AMP       0x26  /*'&'*/
#define CONST_LPAREN    0x28  /*'('*/
#define CONST_RPAREN    0x29  /*')'*/
#define CONST_STAR      0x2A  /*'*'*/
#define CONST_CARROT    0x5E  /*'^'*/
#define CONST_VBAR      0x7C  /*'|'*/

/*UnicodeCChar nextTok(const UnicodeCChar* w) {
    SAGITTARIUS_ASSERT(*w != UC_Null);
    return *(++w);
}*/

//#define PRINT_EXPANSIONS

#ifdef PRINT_EXPANSIONS
    #define USING_EXPANSION(x) printf(x); printUnicodeBuf(state->w); printf("\n");
#else
    #define USING_EXPANSION(x)
#endif

#define DISTRIBUTE_NEGATIVE

//-- Node Utilities --

/// re_createNode
expNode* re_createNode() {
    expNode* n = malloc(sizeof(expNode));
    SAGITTARIUS_ASSERT(n && "re_createNode: malloc failure");
    
    //init cs
    n->cs = NULL;
    
    //initialize members:
    n->n1 = NULL;
    n->n2 = NULL;
    
    //initialize properties
    n->inv = 0;
    n->closed = 0;
    //n->empty = 0;
    //no initializer for e->nullable
    
    return n;
}

/// re_destroyNode
expNode* re_destroyNode(expNode* n) {
    //run on children first
    if(n->n1) {
        n->n1 = re_destroyNode(n->n1);
    }
    if(n->n2) {
        n->n2 = re_destroyNode(n->n2);
    }
    if(n->cs)
        free(n->cs);
    free(n);
    return NULL;
}

/// re_deepCopy
expNode* re_deepCopy(expNode* n) {
    AN(n && "Cannot copy node that does not exist");
    expNode* r = re_createNode();
    r->cs = re_copyCS(n->cs);
    r->inv = n->inv;
    r->closed = n->closed;
    //r->empty = n->empty;
    r->nullable = n->nullable;
    if(n->n1) r->n1 = re_deepCopy(n->n1);
    if(n->n2) r->n2 = re_deepCopy(n->n2);
    return r;
}

/// re_constructSymbol
expNode* re_constructSymbol(const UnicodeCChar c) {
    expNode* n = re_createNode();
    n->type = AST_NODE_TYPE_PRIMITIVE;
    n->cs = re_createCSSym(c);
    return n;
}

/// re_isEmptyString
int re_isEmptyString(expNode* n) {
    if(n->type == AST_NODE_TYPE_PRIMITIVE && re_CSSymComp(n->cs,CONST_RE_EMPTY_STR))
        return 1;
    else
        return 0;
}

/// re_isNull
int re_isNull(expNode* n) {
    if(n->type == AST_NODE_TYPE_PRIMITIVE && re_CSSymComp(n->cs,CONST_RE_NULL))
        return 1;
    else
        return 0;
}

/// re_compare
int re_compare(expNode* r, expNode* s) {
    if(r->type != s->type)
        return 0;
    if(r->type == AST_NODE_TYPE_PRIMITIVE) {
        if(!re_CSeq(r->cs,s->cs))
            return 0;
        AT(r->n1!=NULL && r->n2!=NULL && s->n1!=NULL && s->n2!=NULL); //"Make sure primitives have no children"
        return 1; //for primitives we can return an early success if they match
    }
    if(r->inv != s->inv) return 0;
    if(r->closed != s->closed) return 0;
    //if(r->empty != s->empty) return 0; now represented by letting l1 = l2 = CONST_RE_NULL
    //if(r->nullable != s->nullable) return 0; //derived property
    //compare child n1
    if(r->n1) {
        if(!s->n1)
            return 0;
        if(!re_compare(r->n1, s->n1)) return 0;
    }
    //compare child n2
    if(r->n2) {
        if(!s->n2)
            return 0;
        if(!re_compare(r->n2, s->n2)) return 0;
    }
    return 1; //true if no tests failed
}

//#define DISCARD_UNUSED_NODES

/// re_constructNodeUnionN
expNode* re_constructNodeUnionN(expNode* r, expNode* s) {
    expNode* n;
    
    if(re_isNull(r)) {
        if(re_isNull(s)) {
            //both null
            free(r);
            free(s);
            return re_constructSymbol(CONST_RE_NULL);
        }
        free(r);
        return s;
    }
    if(re_isNull(s) || re_compare(r,s)) {
        free(s);
        return r;
    }
    n = re_createNode();
    n->type = AST_NODE_TYPE_UNION;
    n->n1 = r;
    n->n2 = s;
    return n;
}

/// re_constructNodeConcatN
expNode* re_constructNodeConcatN(expNode* r, expNode* s) {
    expNode* n;
    
    if(re_isEmptyString(r)) {
        free(r);
        return s;
    }
    if(re_isEmptyString(s)) {
        free(s);
        return r;
    }
    if(re_isNull(r) || re_isNull(s)) {
        free(r);
        free(s);
        return re_constructSymbol(CONST_RE_NULL);
    }
    
    n = re_createNode();
    n->type = AST_NODE_TYPE_CONCAT;
    n->n1 = r;
    n->n2 = s;
    return n;
}

/// re_constructNodeInt
expNode* re_constructNodeIntN(expNode* r, expNode* s) {
    expNode* n;
    
    if(re_isNull(r) || re_isNull(s)) {
        free(r);
        free(s);
        return re_constructSymbol(CONST_RE_NULL);
    }
    if(re_compare(r,s)) {
        free(s);
        return r;
    }
    //if one term is a primitive, then may be able to resolve expression as a primitive
    n = re_createNode();
    n->type = AST_NODE_TYPE_INTERSECTION;
    n->n1 = r;
    n->n2 = s;
    return n;
}

//-- Node diagnostics --

/// re_printNodeType
void re_printNodeType(REAST_NodeType type) {
    switch(type) {
        case AST_NODE_TYPE_PRIMITIVE:
            printf("Primitive");
            break;
        case AST_NODE_TYPE_CONCAT:
            printf("Concat");
            break;
        case AST_NODE_TYPE_UNION:
            printf("Union");
            break;
        case AST_NODE_TYPE_INTERSECTION:
            printf("Intersection");
            break;
        default:
            printf("Unknown");
    }
}

/// re_printOut
void re_printOut(expNode* n, int n_indent) {
    int i;
    
    // leading indent
    for(i=0; i<n_indent; ++i) printf(" ");
    
    //negation
    if(n->inv) printf("^");
    //type
    re_printNodeType(n->type);
    //symbol info for primitives
    if(n->type == AST_NODE_TYPE_PRIMITIVE) {
        printf("(");
        re_printCS(n->cs);
        printf(")");
    }
    //closure
    if(n->closed) printf("*");
    
    //nullable
    printf(" ");
    printf("ν = %d", n->nullable);
    
    printf("\n");
    
    //propagate to children
    if(n->n1) {
        re_printOut(n->n1, n_indent+2);
    }
    if(n->n2) {
        re_printOut(n->n2, n_indent+2);
    }
}
